package helper.base

import helper.base.CloneHelper.Cloneable
import helper.base.LHMHelper.A3LHM

// 定义一个用于辅助状态管理的对象
object StateHelper {
    // 抽象的状态类，实现了 Cloneable 接口
    abstract class State : Cloneable {
        open var name: String = "" // 状态的名称
    }

    // 代表事件选择的类，也实现了 Cloneable 接口
    open class EventChoice(val event: String) : Cloneable {
        // 提供一个静态方法来创建 EventChoice 实例
        companion object {
            fun makeEventChoice(event: String) = EventChoice(event)
        }

        // 返回选择的事件名称
        fun getChosenEvent() = event

        // 实现克隆方法
        override fun clone() = makeEventChoice(event)
    }

    // 抽象的转换类，表示从一个状态通过某个事件选择到另一个状态的转换
    abstract class Transition<out E : State, out F : EventChoice>(
        open val start: E, // 开始状态
        open val eventChoice: F, // 事件选择
        open val end: E, // 结束状态
    ) : Cloneable {
        // 获取选定的事件名称
        val chosenEvent: String
            get() = eventChoice.getChosenEvent()
    }

    // 状态-事件-转换的 LinkedHashMap，用于存储和管理状态转换信息
    open class StateEventTransitionLHM<E : State, F : EventChoice, G : Transition<E, F>> : A3LHM<E, F, G>() {
        // 将存储的状态转换信息格式化为字符串
        open fun touchPrintlnType1(): String {
            val sb = StringBuilder()
            this.touch { a1, a2, a3 ->
                sb.append("${a1.name},${a2.getChosenEvent()},${a3.end.name}\n")
            }
            return sb.toString()
        }
    }

    // 状态-事件-概率的 LinkedHashMap，用于存储状态和事件的概率信息
    open class StateEventPLHM<E : State, F : EventChoice> : A3LHM<E, F, Double>() {
        // 将存储的概率信息格式化为字符串
        open fun touchPrintlnType1(): String {
            val sb = StringBuilder()
            this.touch { a1, a2, a3 ->
                sb.append("${a1.name},${a2.getChosenEvent()},${a3}\n")
            }
            return sb.toString()
        }
    }

    // 表示路径的类，包含一系列的转换和整体的奖励值
    open class Path<E : Transition<State, EventChoice>>(
        var pReward: Double = 1.0, // 路径的奖励值
    ) : ArrayList<E>() {
        // 添加一个转换到路径中，并更新奖励值
        fun add(transition: E, pReward: Double = 1.0) {
            this.add(transition)
            this.pReward *= pReward
        }
    }

    // 表示状态机的图结构，包含开始状态、转换列表和最终状态集
    open class Graph<E : State, F : EventChoice, G : Transition<E, F>>(
        val start: E, // 开始状态
        val ts: ArrayList<G>, // 转换列表
        val finalStates: LinkedHashSet<E> = LinkedHashSet(), // 最终状态集
    ) {
        val stateLHS = LinkedHashSet<E>() // 存储状态的 LinkedHashSet
        val stateEventTLHM = StateEventTransitionLHM<E, F, G>() // 状态-事件-转换映射
        var stateEventPLHM = StateEventPLHM<E, F>() // 状态-事件-概率映射

        // 初始化块，用于构建状态机的图结构
        init {
            this.ts.map {
                arrayListOf(it.start, it.end).map {
                    this.stateLHS.add(it)
                }
            }
            ts.map {
                this.stateEventTLHM.addIfAbsent(it.start, it.eventChoice, it)
            }
            this.stateEventPLHM = StateEventPLHM<E, F>().also {
                this.stateEventTLHM.touch { zoneState, s, _ ->
                    it.addIfAbsent(zoneState, s, 1.0)
                }
            }
        }

        // 深度优先搜索（DFS）遍历图，将遍历结果格式化为字符串
        fun dfsTouchType1(
            nowZT: G? = null, // 当前转换
            tabSize: Int = 0, // 缩进大小
            visited: LinkedHashSet<G> = LinkedHashSet(), // 已访问的转换集
            sb: StringBuilder = StringBuilder(), // 结果字符串构建器
        ): String {
            val isNowZTNull = nowZT == null
            if (isNowZTNull) {
                sb.append("${start.name}\n")
            } else {
                nowZT!!.let {
                    sb.append("${"\t".repeat(tabSize)}${it.chosenEvent} ${it.end.name}\n")
                }
            }
            val nowState: E = if (isNowZTNull) {
                start
            } else {
                nowZT!!.end
            }
            if (!isNowZTNull) {
                visited.add(nowZT!!)
            }
            stateEventTLHM[nowState]?.map { (_, a3) ->
                if (visited.contains(a3)) return@map
                dfsTouchType1(
                    a3,
                    tabSize + 1,
                    visited,
                    sb,
                )
            }
            return sb.toString()
        }
    }
}